/**
 * @file    uart_config.c
 *
 * @author  Li.Hua <lihua_338@163.com>.
 *
 * @date    2017-03-01
 *
 * @brief
 *
 * @note
 *
 * Modification history
 * ----------------------------------------------------------------------------
 * Date         Version  Author       History
 * ----------------------------------------------------------------------------
 *
 */

#include "uart_config.h"

#include <errno.h>
#include <stdio.h>
#include <termios.h>
#include <time.h>
#include <limits.h>
#include <sys/time.h>

#include "sysfile.h"

struct speed_map {
	unsigned int speed;
	unsigned int value;
};

static const struct speed_map speeds[] = {
    {B0, 0},
    {B50, 50},
    {B75, 75},
    {B110, 110},
    {B134, 134},
    {B150, 150},
    {B200, 200},
    {B300, 300},
    {B600, 600},
    {B1200, 1200},
    {B1800, 1800},
    {B2400, 2400},
    {B4800, 4800},
    {B9600, 9600},
#ifdef  B19200
    {B19200, 19200},
#elif defined(EXTA)
    {EXTA, 19200},
#endif
#ifdef  B38400
	{B38400, 38400},
#elif defined(EXTB)
	{EXTB, 38400},
#endif
#ifdef B57600
	{B57600, 57600},
#endif
#ifdef B115200
	{B115200, 115200},
#endif
#ifdef B230400
	{B230400, 230400},
#endif
#ifdef B460800
	{B460800, 460800},
#endif
#ifdef B921600
	{B921600, 921600},
#endif
	{B1000000, 1000000},
	{B1500000, 1500000},
	{B2000000, 2000000},
	{B2500000, 2500000},
	{B3000000, 3000000},
	{B3500000, 3500000},
	{B4000000, 4000000},
};

#define NUM_SPEEDS      (int)(sizeof(speeds)/sizeof(speeds[0]))

/******************************************************************************
 * Function:        tty_baud_to_value
 *
 * DESCRIPTION: 将Bxxxx定义转换成数值xxxx
 *
 * Input:       波特率定义，例如B115200, B9600, ...
 *
 * Output:      无
 *
 * Returns:     0 : 未定义的波特率
 *              其它 : 波特率数值
 *
 * Modification history
 * ----------------------------------------------------------------------------
 *  Li.Hua  2008-09-05 12:07:20 ,  Written
 ******************************************************************************/
unsigned int tty_baud_to_value(speed_t speed)
{
    int i = 0;

    do {
        if (speed == speeds[i].speed) {
            return speeds[i].value;
        }
	} while (++i < (int)NUM_SPEEDS);

    return 0;
}

/******************************************************************************
 * Function:        tty_value_to_baud
 *
 * DESCRIPTION: 将波特率数值转换成波特率定义
 *
 * Input:       value : 波特率数值，例如 115200, 9600, ...
 *
 * Output:      无
 *
 * Returns:     0xFFFFFFFF : 不支持改波特率
 *              其它 : 波特率定义
 *
 * Modification history
 * ----------------------------------------------------------------------------
 *  Li.Hua  2008-09-05 12:09:58 ,  Written
 ******************************************************************************/
speed_t tty_value_to_baud(unsigned int value)
{
    int i = 0;

    do {
        if (value == tty_baud_to_value(speeds[i].speed)) {
            return speeds[i].speed;
        }
	} while (++i < (int)NUM_SPEEDS);

    return (speed_t) ~0;
}

/******************************************************************************
 * Function:        set_tty_speed
 *
 * DESCRIPTION: 设置串口通信速率
 *
 * Input:       fd : 打开串口的文件句柄
 *              speed : 串口速度，见name_arr中列举的数值
 *
 * Output:      无
 *
 * Returns:     0    : 设置成功
 *              其它 : 设置失败
 *
 * Modification history
 * ----------------------------------------------------------------------------
 *  Li.Hua  2008-09-04 16:12:30 ,  Written
 ******************************************************************************/
int set_tty_speed(int fd, int speed)
{
    int status;
    speed_t value;
    struct termios   Opt;

    tcgetattr(fd, &Opt);
    value = tty_value_to_baud(speed);
    if (value != (speed_t)~0) {
        tcflush(fd, TCIOFLUSH);
        cfsetispeed(&Opt, value);
        cfsetospeed(&Opt, value);
        status = tcsetattr(fd, TCSANOW, &Opt);
        if (status != 0) {
            return -1;
        }
        tcflush(fd, TCIOFLUSH);
        return 0;
    }

    return -1;
}

/******************************************************************************
 * Function:    set_tty_property
 *
 * DESCRIPTION: 设置串口属性
 *
 * Input:       fd : 打开串口句柄
 *              databits : 数据位(5,6,7,8)
 *              parity   : 校验 (N,E,O,S)
 *              stopbits : 停止位 (1,2)
 *
 * Output:      无
 *
 * Returns:     0    : 成功
 *              其它 : 错误返回码
 *
 * Modification history
 * ----------------------------------------------------------------------------
 *  Li.Hua  2008-09-04 16:15:25 ,  Written
 ******************************************************************************/
int tty_property_config(int fd, int baudrate, int databits, int parity, int stopbits, int flow)
{
    struct termios options;
    speed_t value;

    if (tcgetattr(fd, &options)  !=  0)
        return -EFAULT;

    value = tty_value_to_baud(baudrate);
    if (value != (speed_t)~0) {
        cfsetispeed(&options, value);
        cfsetospeed(&options, value);
    } else
        return -EINVAL;

    options.c_cflag &= ~CSIZE;
    switch (databits) {
        case 5:
            options.c_cflag |= CS5;
            break;
        case 6:
            options.c_cflag |= CS6;
            break;
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            fprintf(stderr, "Unsupported data size\n");
            return -EINVAL;
    }

    switch (parity) {
        case 'n':
        case 'N':
            options.c_cflag &= ~PARENB;   /* Clear parity enable */
            options.c_iflag &= ~INPCK;     /* Enable parity checking */
            break;
        case 'o':
        case 'O':
            options.c_cflag |= (PARODD | PARENB);
            options.c_iflag |= INPCK;             /* Disnable parity checking */
            break;
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;     /* Enable parity */
            options.c_cflag &= ~PARODD;
            options.c_iflag |= INPCK;       /* Disnable parity checking */
            break;
        case 'S':
        case 's':  /*as no parity*/
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            options.c_iflag |= INPCK;
            break;
        default:
            fprintf(stderr, "Unsupported parity\n");
            return -EINVAL;
    }

    switch (stopbits) {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            fprintf(stderr, "Unsupported stop bits\n");
            return -EINVAL;
    }

    switch (flow) {
        case 'h':
        case 'H':
            options.c_cflag |= CRTSCTS;
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            break;
        case 's':
        case 'S':
            options.c_cflag &= ~CRTSCTS;
            options.c_iflag |= IXON | IXOFF | IXANY;
            break;
        case 'n':
        case 'N':
            options.c_cflag &= ~CRTSCTS;
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            break;
        default:
            fprintf(stderr, "Unsupported flow\n");
            return -EINVAL;
    }

    options.c_lflag &= ~(ICANON);
    options.c_lflag &= ~(ISIG | ECHO | ECHOK | ECHOCTL | ECHONL | ECHOPRT);
    options.c_oflag &= ~(ONLCR | OCRNL | ONOCR | ONLRET | NOFLSH | TOSTOP);
    options.c_iflag &= ~(BRKINT | ICRNL | INLCR | IGNCR);
    options.c_iflag |= IGNBRK;
    options.c_cflag |= CREAD | CLOCAL;

    options.c_cc[VTIME] = 0;
    options.c_cc[VMIN] = 1; /* Update the options and do it NOW */

    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        perror("SetupSerial 3");
        return -EFAULT;
    }

    return 0;
}

int main(void)
{
	char buf[128] = {0};
	int rlen = 0;

   struct timeval begin, end, delta;


    int fd = sys_open("/dev/ttyUSB0", OF_RDWR | OF_NOCTTY, 0);
    if (fd >= 0) {
        tty_property_config(fd, 921600, 8, 'n', 1, 'n');
    }	
	while(1)
	{
   		gettimeofday(&begin, NULL);
		rlen = sys_read(fd, buf, 10);
		if (rlen != -1) {
			printf("rlen:%d\n", rlen);
		}
			gettimeofday(&end, NULL);
		    timersub(&end, &begin, &delta);
			printf("delta  %ld(s),  %ld(ms)\n",	delta.tv_sec, (delta.tv_sec * 1000 + delta.tv_usec / 1000));
	//sleep(2);
	}
	return 0;
}
